/+
Create a WeakReference to an Object such that the GC will not be prevented
from collecting the referenced object.

Thanks to Bill Baxter for pointing me in the right direction.

Copyright 2007 Myron Alexander

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+/

import std.stdio : writefln;
import std.string : str = toString;
import std.outofmemory : _d_OutOfMemory;
import stdlib = std.c.stdlib : malloc, free;

class WeakObjectReference(T : Object) {

   this (T obj) {
      /* Get a pointer to the object referenced. The pointer is necessary as
       * I do not what type to declare a pointer to a reference (ie T*->T).
       * I tried T*T but that doesn't work. The T** implementation works but
       * out of curiosity, it would be nice to know if something like T*T could
       * be done.
       */
      T* p = cast(T*)obj;

      /* Allocate a non-gc region of memory to store the pointer to the object.
       * The type is T** as it is: memptr -> T* -> obj.
       */
      weakObjRef = cast(T**)stdlib.malloc (p.sizeof);

      if (null == weakObjRef) {
         _d_OutOfMemory ();
      }

      debug {
         writefln ("%s", weakObjRef);
         writefln ("%s", p);
         writefln ("%s", &obj);
      }

      /* Set memptr->T* = address of obj reference.
       */
      *weakObjRef = p;

      /* Request that the GC call the unhook delegate when the referenced object
       * is collected. The unhook delegate removes the pointer to the object.
       * This is very important otherwise we would maintain a pointer to a
       * non-existant object.
       */
      obj.notifyRegister (&unhook);
   }

   ~this () {
      debug {
         writefln ("Destructing weak reference ...");
      }
      /* Remove the object destruction notification delegate as we will no
       * longer maintain a link to that object.
       */
      if (*weakObjRef != null) {
         debug {
            writefln ("... Unhooked ...");
         }
         (cast(T)(*weakObjRef)).notifyUnRegister (&unhook);
         *weakObjRef = null;
      }

      /* Free the non-gc memory allocated in the constructor.
       */
      stdlib.free (weakObjRef);

      debug {
         writefln ("... Done");
      }
   }

   T get () {
      debug {
         writefln ("Getting pointer: %s", /*weakObjRef == null ? "NULL" :*/ *weakObjRef);
      }
      return cast(T)(*weakObjRef);
   }

   private void unhook (Object obj) {
      debug {
         writefln ("Unhook object");
      }
      *weakObjRef = null;
   }

   private T** weakObjRef;
}

class AnException : Exception {
   this (string msg) {
      super (msg);
   }
}

void main () {

   AnException x = new AnException ("Boom");
   writefln ("%s", &x);
   writefln ("%s", cast (AnException*)x);
   AnException *xp = cast (AnException*)(x);
   writefln ("EX0: %s", (cast(AnException)xp).toString);

   auto w = new WeakObjectReference!(AnException) (x);
   //xp = w.get ();
   AnException y = w.get ();
   if (y !is null) {
      //AnException xx = cast(AnException)xp;
      writefln ("EX1: %s", y.toString());
   }
   delete x;
   y = w.get ();
   if (y !is null) {
      writefln ("EX2: %s", y.toString());
   } else {
      writefln ("EX2: y is null");
   }
}
